package model.searchers.advancedSearchers.nestedLinkedListSearcher;

import java.io.*;

import model.searchers.advancedSearchers.AdvancedGenericSearcher;

import model.searchers.linkedListSearcher.LinkedList;
import model.searchers.advancedSearchers.nestedLinkedListSearcher.NestedLinkedList;
import utilityClasses.Timer;
import model.searchers.URLList;

/**
 * Class NestedLinkedListSearcher represents a searcher, which can look through a nested linked list 
 * to find all URLs on which a given search word occurs, together with number of occurrences on each URL.
 * The nested linked list is created from a file. 
 * It is furthermore possible to put new data in the nested linked list.
 *
 */
public class NestedLinkedListSearcher extends AdvancedGenericSearcher {

	private NestedLinkedList data = null; 	// reference to first element in the data structure (nested linked list)

	/**
	 * Constructor of NestedLinkedListSearcher.
	 * Create a nested linked list containing data from file and measures time spent on it.
	 * 
	 * @param fileName	file from which to create nested linked list
	 * @param mode		boolean search mode
	 * @param ranking	page ranking mode
	 * @throws IOException
	 */
	public NestedLinkedListSearcher(String fileName, boolean mode, boolean ranking)
			throws IOException {
		Timer.printTime(loadData(fileName));
		super.setBooleanSearch(mode);
		super.setPageRank(ranking);
	}

	/**
	 * Find the URLs on which the specified word occurs together with number of occurrences on each URL
	 * 
	 * @param key search word
	 */
	@Override
	public URLList get(String key) {
		NestedLinkedList temp = this.data;
		while (temp != null) {
			if (temp.getValue() != null && temp.getValue().equals(key)) {
				return temp.getUrl().clone();
			}
			temp = (NestedLinkedList) temp.getNext();
		}
		return null;
	}

	/**
	 * Put a new pair of a search word and the URL on which it occurs to the nested linked list
	 * 
	 * @param key	search word to add
	 * @param value	corresponding URL
	 */
	@Override
	public void put(String key, String value) {
		NestedLinkedList current, newListElement;
		current = data;
		NestedLinkedList existingWord = null;
		if (current != null) {
			existingWord = (NestedLinkedList) current.getFromList(key);
		}
		if (existingWord == null) {
			// The word does not exist so we create a new NestedLinkedList object with a
			// new URLlist object
			newListElement = new NestedLinkedList(key,
					new URLList(value, null), data);
			data = newListElement;
			incrementTotalWordCount();
		} else { // Code for checking URLs
			URLList tmpUrlList = existingWord.getUrl();
			URLList existingURL = (URLList) tmpUrlList.getFromList(value);
			if ( existingURL != null) {
				// The URL exists so we add 1 to the counter
				existingURL.increaseOccurences();
			} else {
				// The URL does not exist so we create a new URLlist object with
				// the URL
				URLList tmpUrl = new URLList(value, tmpUrlList);
				existingWord.setUrl(tmpUrl);
			}
		}
	}

	//to be used in LinkedListSearcher
	@Override
	public void put(String key) {
		throw new UnsupportedOperationException(
				"Not supported, use put(String key, String value)");
	}

	/**
	 * Create a sample of specified size containing (pseudo)random search words from the nested linked list.
	 * To be used for benchmarking
	 * 
	 * @param sampleSize	size of sample
	 * @return	array of search words
	 */
	@Override
	public String[] createSample(int sampleSize) {
		if (data== null) return null;
		String[] sample = new String[sampleSize];
		for (int i = 0; i < sampleSize; i++) {
			int index = (int) (Math.random() * getTotalWordCount());
			LinkedList temp = data.getFromList(index);
			sample[i] = (temp == null) ? null : temp.getValue();
			
		}
		return sample;
	}

	@Override
	public long loadData(String fileName) 	throws IOException {
		// Read the file and create the linked list
		long time = Timer.startTime();
		super.readFile(fileName);
		long total_time = Timer.getTime(time);
		return total_time;
	}
	
	@Override
	public String toString() {
		String result = "Filename: " + this.getFileName() + "\r\nSearcher: " +this.getSearcherType() +  "\r\nBoolean Search: " + this.isBooleanSearch();
		return result;
		
	}
	
	
}